﻿using System;
using System.Collections.Generic;
using System.Text;
using System.Text.RegularExpressions;
using Microsoft.SharePoint.Utilities;
using Kiiro.Common.Entities;
using Kiiro.Common.Utility;
using Kiiro.Validation.Interfaces;

namespace Kiiro.Validation.Validators.Entities
{
    public abstract class BaseEntityValidator : IValidator<BaseEntity>
    {
        private readonly List<ValidationMessage> validationErrors = new List<ValidationMessage> ( );

        #region  IValidator<BaseEntity> Members
        public List<ValidationMessage> Validate ( BaseEntity entityToValidate )
        {
            // If the entity is marked as deleted, don't do any validation, just return.
            if ( entityToValidate.EditType != EditType.Deleted )
            {
                ValidationMessage message = null;

                // Required Fields Checks
                #region required fields checks

                message = IsRequired ( entityToValidate.Title, "Title" );
                if ( message != null )
                {
                    entityToValidate.TitleMessage.Add ( message );
                    validationErrors.Add ( message );
                }



                message = IsRequired ( entityToValidate.CreatedBy, "Created By" );
                if ( message != null )
                {
                    entityToValidate.CreatedByMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                message = IsRequired ( entityToValidate.CreatedDate, "Created Date" );
                if ( message != null )
                {
                    entityToValidate.CreatedDateMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                message = IsRequired ( entityToValidate.ModifiedBy, "Updated By" );
                if ( message != null )
                {
                    entityToValidate.ModifiedByMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                message = IsRequired ( entityToValidate.ModifiedDate, "Updated Date" );
                if ( message != null )
                {
                    entityToValidate.ModifiedDateMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                message = IsRequired ( entityToValidate.SiteCollectionId, "Site Collection Id" );
                if ( message != null )
                {
                    entityToValidate.SiteCollectionIdMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                message = IsRequired ( entityToValidate.ServerRelativeUrl, "Server Relative Url" );
                if ( message != null )
                {
                    entityToValidate.ServerRelativeUrlMessage.Add ( message );
                    validationErrors.Add ( message );
                }


                message = IsRequired(entityToValidate.Description, "Description");
                if (message != null)
                {
                    entityToValidate.DescriptionMessage.Add(message);
                    validationErrors.Add(message);
                }
                #endregion

                // Comparison Checks
                #region comparison checks


                message = NotGreaterThan ( entityToValidate.CreatedDate, entityToValidate.ModifiedDate, "Created Date", "Modified Date" );
                if ( message != null )
                {
                    entityToValidate.CreatedDateMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                message = NotGreaterThan ( entityToValidate.CreatedDate, entityToValidate.ModifiedDate, "Created Date", "Modified Date" );
                if ( message != null )
                {
                    entityToValidate.ModifiedDateMessage.Add ( message );
                    validationErrors.Add ( message );
                }

                if ( !string.IsNullOrEmpty ( entityToValidate.Title ) )
                {
                    message = CheckMaximumLength ( 255, entityToValidate.Title, "Title" );

                    if ( message != null )
                    {
                        entityToValidate.TitleMessage.Add ( message );
                        validationErrors.Add ( message );
                    }

                    message = HasScriptingTagsInText ( entityToValidate.Title, "Title" );

                    if ( message != null )
                    {
                        entityToValidate.TitleMessage.Add ( message );
                        validationErrors.Add ( message );
                    }
                }
                #endregion
            }

            return validationErrors;
        }

        #endregion

        protected ValidationMessage IsRequired ( int value, string propertyName )
        {
            ValidationMessage message = null;

            if ( value == default ( int ) )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }

        protected ValidationMessage IsRequired ( Guid value, string propertyName )
        {
            ValidationMessage message = null;

            if ( value == default ( Guid ) || value == null )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }

        protected ValidationMessage IsRequired ( User value, string propertyName )
        {
            ValidationMessage message = null;
            if ( value == null )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }

        protected ValidationMessage IsRequired(List<int> value, string propertyName)
        {
            ValidationMessage message = null;
            if (value == default(List<int>) || value == null)
            {
                message = new ValidationMessage(ValidationSeverity.Validation, string.Format("{0} is a required field.", propertyName));
            }
            return message;
        }

        protected ValidationMessage IsRequired ( DateTime value, string propertyName )
        {
            ValidationMessage message = null;
            if ( value == default ( DateTime ) )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }

        protected ValidationMessage IsRequired ( string value, string propertyName )
        {
            ValidationMessage message = null;
            if ( string.IsNullOrEmpty ( value.Trim( ) ))
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }

        protected ValidationMessage IsRequired ( List<string> value, string propertyName )
        {
            ValidationMessage message = null;
            if ( value == default ( List<string> ) || value == null )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }


        protected ValidationMessage IsRequired ( float value, string propertyName )
        {
            ValidationMessage message = null;

            if ( value == default ( float ) )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is a required field.", propertyName ) );
            }
            return message;
        }

        /// <summary>
        /// Validate if email is in the correct format.
        /// </summary>
        /// <param name="value">The first value to compare.</param>
        /// <param name="peopertyName">The property name of the first value.</param>
        protected ValidationMessage IsEmailValid ( string value, string propertyName )
        {
            ValidationMessage message = null;

            string pattern = @"([\w\-]+)@([\w\-]+)\.([\w\-]+)";            

            if ( !Regex.IsMatch ( value.Trim(),pattern ) || value.Trim().Contains(" ") )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} is an invalid email format.", propertyName) );
            }
            return message;
        }

        /// <summary>
        /// Check to see that the first value is not greater than the second value.
        /// </summary>
        /// <param name="firstValue">The first value to compare.</param>
        /// <param name="firstValueName">The property name of the first value.</param>
        /// <param name="secondValue">The second value to compare.</param>
        /// <param name="secondValueName">The property name of the second value.</param>
        protected ValidationMessage NotGreaterThan ( DateTime firstValue, DateTime secondValue, string firstValueName, string secondValueName )
        {
            ValidationMessage message = null;
            if ( firstValue > secondValue )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} cannot be after {1}.", firstValueName, secondValueName ) );
            }
            return message;
        }

        ///<summary>
        ///Check to see if the value is between 0 and 100
        /// </summary>
        /// <param name="value">The first value to compare.</param>
        /// <param name="valueName">The property name.</param>
        protected ValidationMessage NotBetween ( float value, string valueName )
        {
            ValidationMessage message = null;
            if ( 0 > value || value > 100 )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} must be between 0 and 100.", valueName ) );
            }
            return message;
        }

        /// <summary>
        /// Check to see that the first value is not less than the second value.
        /// </summary>
        /// <param name="firstValue">The first value to compare.</param>
        /// <param name="firstValueName">The property name of the first value.</param>
        /// <param name="secondValue">The second value to compare.</param>
        /// <param name="secondValueName">The property name of the second value.</param>
        protected ValidationMessage NotLessThan ( DateTime firstValue, DateTime secondValue, string firstValueName, string secondValueName )
        {
            ValidationMessage message = null;
            if ( firstValue < secondValue )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} cannot be after {1}.", firstValueName, secondValueName ) );
            }
            return message;
        }

        /// <summary>
        /// Check to see that the value is greater than zero
        /// </summary>
        /// <param name="firstValue">The value to compare.</param>
        /// <param name="firstValueName">The property name of the value.</param>
        protected ValidationMessage NotNegative ( float value, string valueName )
        {
            ValidationMessage message = null;
            if ( value < 0 )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} cannot be less than zero.", valueName ) );
            }
            return message;
        }

        protected ValidationMessage CheckMaximumLength ( int max, string value, string propertyName )
        {
            ValidationMessage message = null;
            if ( max < value.Length )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} cannot be more than {1} characters.", propertyName, max ) );
            }
            return message;
        }

        protected ValidationMessage CheckMaximumLength ( int max, long value, string propertyName )
        {
            ValidationMessage message = null;
            if ( max < value )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} cannot be more than {1} characters.", propertyName, max ) );
            }
            return message;
        }

        /// <summary>
        /// Check to see that work estimated time is not greator than 5262480 minutes (10 years, counting two leap years).
        /// </summary>
        /// <param name="value">The value to compare.</param>
        /// <param name="valueName">The property name of the value.</param>
        /// <param name="maxValue">The maximum value for time estimated</param>
        protected ValidationMessage NotGreaterThanTenYears ( float value, string valueName, double maxValue, double hoursPerDay )
        {
            ValidationMessage message = null;
            if ( value < 0 || value > maxValue )
            {
                double hours = maxValue / 60;
                double days = hours / hoursPerDay;
                double weeks = days / 5;
                double months = weeks / 4;
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} must be greater than 0 and less than {1} minutes, {2} hours, {3} days, {4} weeks or {5} months (10 years based on working hours per day).", valueName, maxValue, hours, days, weeks, months ) );
            }
            return message;
        }

        /// <summary>
        /// Checks for any HTML/Script tags within the string
        /// </summary>
        protected ValidationMessage HasScriptingTagsInText ( string value, string propertyName )
        {
            ValidationMessage message = null;
            if ( Regex.IsMatch ( value.Trim ( ), @"(\<[^\>]+\>)", RegexOptions.IgnoreCase ) )
            {
                message = new ValidationMessage ( ValidationSeverity.Validation, string.Format ( "{0} cannot contain script tags.", propertyName ) );
            }
            return message;
        }

        /// <summary>
        /// Checks for invalid characters when saving folder names and project urls
        /// </summary>
        /// <param name="entityToValidate">Populated entity to validate.</param>
        /// <param name="propertyName">The property name of the value.</param>
        protected ValidationMessage HasSpecialCharactersForSiteUrlInText ( BaseEntity entityToValidate, string propertyName )
        {
            ValidationMessage message = null;
            string illegalChar = default ( string );
            string value = default ( string );

            message = HasSpecialCharactersForFileNameOrPathInText ( entityToValidate, propertyName );

            // urls pass the above but need additional validation over filenames
            if ( message == null && ( entityToValidate is Project || ( entityToValidate is Document && ( entityToValidate as Document ).IsFolder ) ) )
            {
                // see KB Article: 905231 --  http://support.microsoft.com/default.aspx?scid=kb;en-us;905231
                //      --> ~ # % & * { } \ : < > ? / + | " _xx .. .xx
                // The group name is empty, or you are using one or more of the following invalid characters: " / \ [ ] : | < > + = ; , ? * ' @
                //      --> " / \ [ ] : | < > + = ; , ? * ' @

                /*** testing ***** 
                 *                     test value: a.@%^&*`$(){}|[]\:;"'<>?,/`.dx._ (fails)
                 *        valid project url value: a.@^`$()[];',`.dx._              (fails)
                 * valid project group name value: a.`!$^()_-._                     (passes, but converts ` to %60 and ^ to %5E) */

                char[ ] invalidSPGroupChars = new char[ ] { '"', '/', '\\', '[', ']', ':', '|', '<', '>', '+', '=', ';', ',', '?', '*', '\'', '@' };

                value = entityToValidate is Project ? ( entityToValidate as Project ).ServerRelativeWebUrl : entityToValidate.Title;

                if ( value.IndexOfAny ( invalidSPGroupChars ) != -1 )
                    illegalChar = value.Substring ( value.IndexOfAny ( invalidSPGroupChars ), 1 );

                //if ( value.IndexOfAny ( System.IO.Path.GetInvalidPathChars ( ) ) != -1 )
                //{
                //    if ( string.IsNullOrEmpty ( illegalChar ) )
                //        illegalChar = value.Substring ( value.IndexOfAny ( System.IO.Path.GetInvalidPathChars ( ) ), 1 );
                //}

                // check for leading underscore (_) on projects and folders
                if ( string.IsNullOrEmpty ( illegalChar ) &&
                    ( entityToValidate is Project || ( entityToValidate is Document && ( entityToValidate as Document ).IsFolder ) ) )
                {
                    if ( value.StartsWith ( "_" ) )
                        illegalChar = "leading underscore (_xx),";
                }

                // check for empty space
                if ( string.IsNullOrEmpty ( illegalChar ) && entityToValidate is Project )
                {
                    if ( value.Trim ( ).Contains ( " " ) )
                        illegalChar = "space ( )";
                }

                if ( !string.IsNullOrEmpty ( illegalChar ) )
                    message = new ValidationMessage (
                        ValidationSeverity.Validation,
                        string.Format ( "{0} is not a valid {2}.<br />{1} is an illegal character.", value, illegalChar.Trim ( ), propertyName ) );
            }

            return message;
        }

        /// <summary>
        /// Checks for invalid characters when saving document or folder names and project urls
        /// </summary>
        /// <param name="entityToValidate">Populated entity to validate.</param>
        /// <param name="propertyName">The property name of the value.</param>
        protected ValidationMessage HasSpecialCharactersForFileNameOrPathInText ( BaseEntity entityToValidate, string propertyName )
        {
            ValidationMessage message = null;
            string illegalChar = default ( string );
            string value = default ( string );

            // get value to check
            if ( entityToValidate is Project )
            {
                value = ( entityToValidate as Project ).ServerRelativeWebUrl;
            }
            else if ( entityToValidate is Document && !( entityToValidate as Document ).IsFolder )
            {
                // which scenario are we running??
                //  (changed to)    a) if txtDocumentTitle.Text.Trim( ) > 50 then.... (w/no extension in calculation)
                //  (previous)      b) if txtDocumentTitle.Text.Trim( ) + "." + extension of 3 chars > 50 then
                value = entityToValidate.Title.Substring ( 0, entityToValidate.Title.LastIndexOf ( "." ) );
            }
            else
            {
                value = entityToValidate.Title;
            }

            // check value
            if ( SPUrlUtility.IndexOfIllegalCharInUrlLeafName ( value ) != -1 )
            {
                illegalChar = value.Substring ( SPUrlUtility.IndexOfIllegalCharInUrlLeafName ( value ), 1 );

                if ( illegalChar == "." )
                {
                    if ( value.StartsWith ( "." ) ) illegalChar = "leading period (.xx)";
                    if ( value.EndsWith ( "." ) ) illegalChar = "trailing period (xx.)"; // causes [xxx]..[extension]
                    if ( value.Contains ( ".." ) ) illegalChar = "double period (..)";
                }
            }

            //if ( value.IndexOfAny ( System.IO.Path.GetInvalidFileNameChars ( ) ) != -1 )
            //{
            //    if ( string.IsNullOrEmpty ( illegalChar ) )
            //        illegalChar = value.Substring ( value.IndexOfAny ( System.IO.Path.GetInvalidFileNameChars ( ) ), 1 );
            //}

            if ( !string.IsNullOrEmpty ( illegalChar ) )
                message = new ValidationMessage (
                    ValidationSeverity.Validation,
                    string.Format ( "{0} is not a valid {2}.<br />{1} is an illegal character.", value, illegalChar.Trim ( ), propertyName ) );

            return message;
        }

        ///<summary>
        ///Checks if any of the attachments is invalid extension
        ///</summary>
        ///<param name="entityToValidate"> Populated entity to validate</param>
        protected ValidationMessage AreAttachmentsValidExtensions(BaseEntity entityToValidate, Attachment file)
        {
            ValidationMessage message = null;

            if (file.EditType == EditType.Unchanged)
            {
                message = new ValidationMessage(
                    ValidationSeverity.Validation, file.FileName + " is not a valid attachment");
                //entityToValidate.AttachmentListMessage.Add(new ValidationMessage(ValidationSeverity.Validation, file.FileName + " is not a valid attachment"));
            }
            return message;
        }
    }
}

/*************************************************************************
 * 
 * $Log: /Internal Projects/Kiiro/Kiiro.Validation/Validators/Entities/BaseEntityValidator.cs $
 * 
 * 14    15/12/09 12:48p Adamw
 * 
 * 13    15/12/09 12:40p Adamw
 * Case 411 added validation for invalidating leading underscore in folder
 * name
 * 
 * 12    12/14/09 2:10p Shafia
 * attachments file extensions validation for discussions, tasks, and
 * issues
 * 
 * 11    10/26/09 11:59a Adamw
 * Case 332
 * 
 * 10    10/26/09 11:47a Adamw
 * Case 332
 * 
 * 9     10/26/09 11:08a Adamw
 * Case 332
 * 
 * 8     10/23/09 1:38p Adamw
 * added script tag check, Case 332, and moved url, folder and filemname
 * validation to a central location to reduce duplicate code
 * 
 * 7     21/10/09 11:40a Shafia
 * 
 * 6     16/10/09 1:57p Shafia
 * IsRequired that checks for string values now trims the value to ensure
 * no white spaces are accepted at end or start of text
 * 
 * 5     9/03/09 3:16p Stefanf
 * 
 * 4     17/08/09 10:45a Camerons
 * Added check-in comments section
 * 
 ************************************************************************/